var program = require('commander');
var packjson = require("./package.json");
var jv = require("libjv").default;
require("./comm");

var cmd = program
    .version(packjson.version)
    .description(packjson.description)
    .usage('<--port 本代理端口号> <--proxy 代理地址>')
    .option('--port <本代理端口号>', "默认是 5050")
    .option('--proxy <代理目标地址>', "")
    .option('--eachLog [每个请求记录到一个单独的文件]', "默认为 false")
    .option('--encoding [Response默认编码]', "仅在 Response.header 没有 content-type 时有效,默认为 utf8")
    .option('--log-Path [日志位置]', "默认 ./")
    // .option('--urlFilter [Url自定义回调]', "lambda表达式,参数 {path,json,hash}.")
    .option('--origin [修改origin]')
    .option('--allowHeaders [跨域允许的Headers]')
    .option('--requestFilter [Request自定义回调]', "lambda表达式,参数 req，如：'if( req.headers['compressed'] ) req.header[''] = ''; ")
    .option('--responseFilter [Response自定义回调]', "lambda表达式,参数 resp，如：'if( resp.headers['compressed'] ) resps.header[''] = ''; ")
    .parse(process.argv);

var http = require('http');
var httpProxy = require('http-proxy');
var zlib = require('zlib');
let iconv = require('iconv-lite');
var getRawBody = require('raw-body')

var proxy_target_host = cmd.proxy;
var server_port = cmd.port || 5050;
var logFile = (cmd.logPath || "./") + "http-proxy.log";
// var urlFilter = cmd.urlFilter || "";
var request_filter = cmd.requestFilter || "";
var allowHeaders = cmd.allowHeaders || "";
var response_filter = cmd.responseFilter || "";
var origin = cmd.origin || "";
var encoding = cmd.encoding || "utf8";

var eachLog = jv.asBoolean(cmd.eachLog, false);

const line_break = "\r\n";

jv.logFile = logFile;
jv.eachLog = eachLog;

if (request_filter) {
    try {
        var rf = "(req)=> { " + request_filter + "}";
        console.log(rf);
        request_filter = eval(rf);
    } catch (e) {
        console.error("requestFilter参数错误:" + e.message);
        process.exit(1);
        return 1;
    }
}
if (response_filter) {
    try {
        var rf = "(resp)=> { " + response_filter + "}";
        console.log(rf);
        response_filter = eval(rf);
    } catch (e) {
        console.error("responseFilter参数错误:" + e.message);
        process.exit(1);
        return 1;
    }
}


if (!cmd.proxy) {
    cmd.outputHelp();
    process.exit(1);
    return 1;
}


var proxy = httpProxy.createProxyServer({followRedirects: true, changeOrigin: true});

proxy.on('error', function (err, req, res) {
    res.writeHead(500, {
        'Content-Type': 'text/plain'
    });

    res.end('error');
});

proxy.on("proxyReq", function (proxyReq, req, res, options) {

    // var body = Buffer.from("");
    // proxyReq.on('data', function (data) {
    //     body = Buffer.concat([body, data]);
    // });

    req.startAt = Date.now();
    req.msgs = [];

    req.msgs.pushAll([
        "[[[---> " + req.startAt.toDateString("yyyyMMdd-HHmmss.fff") + line_break + req.method + " " + req.url,
        JSON.stringify(jv.query2Json(req.url)),
        "---",
        Object.keys(req.headers).map(key => "\t" + key + ": " + req.headers[key]).join("\n"),
        "[request body]:"
    ]);


    var contentType = req.headers["content-type"] || "";
    var the_encoding = jv.getEncoding(contentType) || encoding;


    getRawBody(req).then(function (data) {
        if (request_filter) {
            request_filter(req);
        }

        var requestBody = data;

        if (req.headers["content-encoding"]) {
            requestBody = iconv.decode(zlib.unzipSync(requestBody), the_encoding);
        } else {
            requestBody = iconv.decode(requestBody, the_encoding);
        }

        var contentType = req.headers["content-type"] || "";
        if (!contentType.includes("text") && !contentType.includes("json")) {
            requestBody = "";
        }

        if (requestBody) {
            req.msgs.push(requestBody);
        }
    });

});

proxy.on('proxyRes', function (proxyRes, req, res) {

    var origin = req.origin;
    if (origin) {
        var allowHeaders_ary = allowHeaders.split(",").filter(it => it);
        var requestHeaders_ary = (req.headers["access-control-request-headers"] || "").split(",").filter(it => it);

        allowHeaders_ary.pushAll(requestHeaders_ary);

        proxyRes.headers["access-control-allow-headers"] = Array.from(new Set(allowHeaders_ary)).join(",");
        proxyRes.headers["access-control-allow-origin"] = origin;
        proxyRes.headers["access-control-allow-methods"] = "GET,POST,PATCH,PUT,HEAD,OPTIONS,DELETE";
        proxyRes.headers["access-control-allow-credentials"] = "true";
    }

    var body = Buffer.from("");
    proxyRes.on('data', function (data) {
        body = Buffer.concat([body, data]);
    });
    proxyRes.on('end', function () {
        if (response_filter) {
            response_filter(proxyRes);
        }
        var bodyString = "";
        var contentType = proxyRes.headers["content-type"] || "";

        var the_encoding = jv.getEncoding(contentType) || encoding;

        if (proxyRes.headers["content-encoding"]) {
            bodyString = iconv.decode(zlib.unzipSync(body), the_encoding);
        } else {
            bodyString = iconv.decode(body, the_encoding);
        }

        if (!contentType.includes("text") && !contentType.includes("json")) {
            bodyString = "";
        }


        // var cookies = proxyRes.headers["set-cookie"] ;
        // var cookies_len = cookies.length;
        // if( cookies_len){
        //     for(var i=0;i<cookies_len;i++){
        //         cookies[i] = cookies[i].replace(/Path=\/([^;]*);/i,"Path=/;")
        //     }
        // }

        var now = Date.now();
        req.msgs.pushAll(["[response: " + proxyRes.statusCode + "] " + (now - req.startAt) + "ms",
            Object.keys(proxyRes.headers).map(key => "\t" + key + ": " + proxyRes.headers[key]).join("\n"),
            "[response body]"
        ]);


        if (bodyString) {
            req.msgs.push(bodyString);
        }

        req.msgs.push("<---]]]");


        if (proxyRes.statusCode != 200 || (req.method != "OPTIONS" && req.method != "HEAD")) {
            req.logToFile(jv.logFile, jv.eachLog);
        }
    });
});

var server = http.createServer(function (req, res, proxy2) {
    req.origin = req.headers["origin"];
    if (req.origin) {
        delete req.headers["origin"];
    }
    req.withCredentials = true;
    proxy.web(req, res, {target: proxy_target_host}, e => {
        req.msgs.push("[Error] " + e.message);
        req.msgs.push(" <---]]]");
        req.logToFile(jv.logFile, jv.eachLog);
    });
});

server.listen(server_port);

console.log("代理中: http://localhost:" + server_port + "  ---> " + proxy_target_host);

